/*
 * 代号：凤凰
 * http://www.jphenix.org
 * 2014-06-04
 * V4.0
 */
package com.jphenix.driver.serialno.instancea;

import com.jphenix.standard.docs.ClassInfo;
import com.jphenix.standard.serialno.ISN;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;


/**
 * 唯一序列号生成类
 * 
 * 序列号组成
 * 
 *   序列号头字符串 + yyMMddHHmmssSSS + 序列号中的索引值 + 递增值
 *   
 *   序列号中的索引值：一组集群中，当前服务器在集群中的位置，避免多台服务器生成同样的值
 * 
 * 2019-01-23 在获取序列号方法上加了同步关键字，因为发现了相同主键值
 * 2019-06-26 重构了该序列号生成器
 * 2019-07-06 修改了序列号头丢失的错误
 *            修改了序列号长度错误
 * 
 * com.jphenix.driver.serialno.instancea.SN
 * 
 * @author 刘虻
 * 2006-1-28  0:27:27
 */
@ClassInfo({"2019-07-06 16:38","唯一序列号生成类"})
public class SN implements ISN {

	/**
	 * @param DEFAULT_MIN_SIZE 序列号最小长度
	 */
    private final static int DEFAULT_MIN_SIZE = 20;

    /**
     * @param index 序列号中的索引值
     */
    private String index = "";
    /**
     * @param snHead 当前类名称标识
     */
    private String snHead;
    /**
     * @param snSize 主键长度
     */
    private int snSize = DEFAULT_MIN_SIZE;
    /**
     * @param tNoLength 流水号长度
     */
    private int tNoLength = 0;
    /**
     * @param tNo 当前流水号
     */
    private long tNo = 0;
    /**
     * @param maxNo 流水号最大值
     */
    private long maxNo = 0;
    /**
     * @param pattern 时间格式 15位
     */
    private final String pattern = "yyMMddHHmmssSSS";

	
	/**
	 * 构造函数
	 * @author MBG
	 */
	public SN() {
        snHead = "PX";
        calc();
	}
	
    /**
     * 构造函数 
     * @author 刘虻
     * 2006-1-28 0:27:27
     */
    public SN(String head,int size) {
        super();
        snSize = size;
        if(head==null || head.length()<1) {
        	head = DEFAULT_HEAD;
        }
        snHead = head;
        calc();
    }
    
    /**
     * 获取新的类实例
     * @author 刘虻
     * 2008-7-31下午05:55:16
     * @return 新的类实例
     */
    public static ISN instance(String name,int size){
    	return new SN(name,size);
    }
    
    /**
     * 序列号生成器索引值
     * @return 当前服务器在集群中的位置
     * 2019年6月26日
     * @author MBG
     */
    @Override
    public String index() {
    	return index;
    }
    
    /**
     * 设置序列号生成器索引值
     * @param index 当前服务器在集群中的位置
     * 2019年6月26日
     * @author MBG
     */
    @Override
    public void index(String index) {
    	if(index!=null) {
    		this.index = index;
    	}
    	calc();
    }
    
    /**
     * 覆盖方法 
     * @author  刘虻
     * 2006-1-28 0:27:27
     */
    @Override
    public String head() {
        return snHead;
    }
    
    /**
     * 计算序列号生成时需要的值
     * 2019年6月26日
     * @author MBG
     */
    private void calc() {
        if(snHead==null) {
        	snHead="";
        }
        if(snSize<1) {
        	snSize = 20;
        }
        tNoLength = snSize-snHead.length()-index.length()-15;
        if(tNoLength<1) {
        	System.err.println("\n\n\n*Exception***************************************************\n"
        			+"The Serial Number Length Less Than 1 head:["+snHead+"] size:["+snSize+"] index:["+index+"]\n"
        			+"****************************************************\n\n\n");
        }
        maxNo = (long)Math.pow(10,tNoLength);
    }
    
    /**
     * 获取一个新的主键
     * @return 一个新的主键
     * 2015年5月14日
     * @author 马宝刚
     */
    @Override
    public String getSID() {
        return new StringBuffer(snHead).append(now()).append(getNum()).toString();
    }
    
    /**
     * 获取一个新的主键
     * @return 一个新的主键
     * 2015年5月14日
     * @author 马宝刚
     */
    @Override
    public String sid() {
        return new StringBuffer(snHead).append(now()).append(getNum()).toString();
    }

    /**
     * 获取递增序列号
     * @return 递增序列号
     * 2019年6月26日
     * @author MBG
     */
    private synchronized String getNum() {
    	tNo++;
    	if(tNo>=maxNo) {
    		tNo = 0;
    	}
    	//构建返回值
        StringBuilder res = new StringBuilder(String.valueOf(tNo));
    	while(res.length()<tNoLength) {
    		res.append("0");
    	}
    	return res.toString();
    }
    
    /**
     * 覆盖方法 
     * @author  刘虻
     * 2006-1-28 0:27:27
     */
    @Override
    public String[] getSIDs(int count) {
    	//构造返回值
    	String[] reSIDs = new String[count];
    	for(int i=0;i<count;i++) {
    		reSIDs[i] = getSID();
    	}
        return reSIDs;
    }

    /**
     * 获取一组序列号
     * @author 刘虻
     * 2007-8-31下午08:23:58
     * @param countInt 序列号
     * @return 一组序列号
     */
    @Override
    public List<String> getSIDList(int countInt) {
    	//构建返回值
    	ArrayList<String> reList = new ArrayList<String>(countInt);
    	for (int i=0;i<countInt;i++) {
    		reList.add(getSID());
    	}
    	return reList;
    }

	/**
	 * 覆盖方法 
	 * @author  刘虻
	 * 2006-7-20 11:36:35
	 */
	@Override
    public int size() {
		return snSize;
	}
	
	
	/**
	 * 获取当前时间字符串
	 * @author 刘虻
	 * 2007-8-31下午07:58:12
	 * @return 当前时间字符串
	 */
	private String now() {
        return new SimpleDateFormat(pattern).format(new Date());
	}
}
